package com.ddzj.scaffold.utils;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Base64;

import java.math.BigInteger;
import java.security.SecureRandom;

/**
 * @Author:yangzibo
 * @CreateTime:2024-05-12 19:20
 * @Description:
 * @Version:1.0
 **/
public class SM2Utils {
    private static final ECNamedCurveParameterSpec SM2_SPEC = ECNamedCurveTable.getParameterSpec("sm2p256v1");

    public static AsymmetricCipherKeyPair generateKeyPair() {
        ECDomainParameters domainParameters = new ECDomainParameters(SM2_SPEC.getCurve(), SM2_SPEC.getG(), SM2_SPEC.getN(), SM2_SPEC.getH());
        ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(domainParameters, new SecureRandom());
        ECKeyPairGenerator generator = new ECKeyPairGenerator();
        generator.init(keyGenerationParameters);
        return generator.generateKeyPair();
    }

    public static byte[] sign(byte[] data, ECPrivateKeyParameters privateKey) throws CryptoException {
        SM2Signer signer = new SM2Signer();
        CipherParameters parameters = new ParametersWithRandom(privateKey, new SecureRandom());
        signer.init(true, parameters);
        signer.update(data, 0, data.length);
        return signer.generateSignature();
    }

    public static boolean verify(byte[] data, byte[] signature, ECPublicKeyParameters publicKey) {
        SM2Signer signer = new SM2Signer();
        signer.init(false, publicKey);
        signer.update(data, 0, data.length);
        return signer.verifySignature(signature);
    }
    // 假设你已经有了 ECPrivateKeyParameters privateKey

    public static String privateKeyToString(ECPrivateKeyParameters privateKey) {
        byte[] privateKeyBytes = privateKey.getD().toByteArray(); // 获取私钥的字节表示
        return Base64.toBase64String(privateKeyBytes); // 使用 Base64 编码转换为字符串
    }
    // 假设你已经有了 ECPublicKeyParameters publicKey

    public static String publicKeyToString(ECPublicKeyParameters publicKey) {
        ECPoint point = publicKey.getQ(); // 获取公钥点
        byte[] publicKeyBytes = point.getEncoded(false); // 获取公钥的字节表示，参数 false 表示不使用压缩格式
        return Base64.toBase64String(publicKeyBytes); // 使用 Base64 编码转换为字符串
    }

    // 假设你已经有了 ECDomainParameters domainParameters

    public static ECPublicKeyParameters stringToPublicKey(String publicKeyString) {
        ECDomainParameters domainParameters = new ECDomainParameters(SM2_SPEC.getCurve(), SM2_SPEC.getG(), SM2_SPEC.getN(), SM2_SPEC.getH());
        byte[] publicKeyBytes = Base64.decode(publicKeyString); // 使用 Base64 解码字符串为字节数组
        ECPoint point = domainParameters.getCurve().decodePoint(publicKeyBytes); // 从字节数组解码公钥点
        return new ECPublicKeyParameters(point, domainParameters); // 构造 ECPublicKeyParameters 对象
    }


    public static ECPrivateKeyParameters stringToPrivateKey(String privateKeyString) {
        ECDomainParameters domainParameters = new ECDomainParameters(SM2_SPEC.getCurve(), SM2_SPEC.getG(), SM2_SPEC.getN(), SM2_SPEC.getH());
        byte[] privateKeyBytes = Base64.decode(privateKeyString); // 使用 Base64 解码字符串为字节数组
        BigInteger privateKeyValue = new BigInteger(1, privateKeyBytes); // 将字节数组转换为大整数
        return new ECPrivateKeyParameters(privateKeyValue, domainParameters); // 使用大整数构造 ECPrivateKeyParameters 对象
    }

    public static void main(String[] args) throws CryptoException {
        AsymmetricCipherKeyPair keyPair = generateKeyPair();
        ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
        ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();

        String message = "Hello, SM2!";
        byte[] messageBytes = message.getBytes();

        byte[] signature = sign(messageBytes, privateKey);

        System.out.println("Signature: " + Base64.toBase64String(signature));

        boolean isValid = verify(messageBytes, signature, publicKey);
        System.out.println("Signature valid: " + isValid);
    }
}
